<template>
  <div class="dialog">
    <el-dialog
      v-model="dialogVisible"
      title="Tips"
      :width="dialogWidth"
      :close-on-click-modal="false"
      @closed="closed"
    >
      <template #title>
        <span>{{titleResult}}</span>
        <el-divider class="title_divider"/>
      </template>
      <el-form ref="formRef"
               :inline="formInline"
               :model="formObject"
               :rules="formRules"
               :label-width="formLabelWidth"
               :label-position="formLabelPosition" >
        <template
          v-for="(item,index) in formHeader"
          :key="index">
          <!--如果有分割线，则显示分页线-->
          <el-divider v-if="item.type === 'divider'" class="foot_divider"/>
          <el-form-item
            v-else
            :label="item.label+':'"
            :prop="item.prop"
            :rules="item.disabled==true?[]:item.rules"
            v-show="item.show!=false"
          >
            <!--如果是自定义插槽类型-->
            <span v-if="item.type === 'custom'">
              <slot :name="item.prop" v-bind:item="item" v-bind:formItem="formObject[item.prop]">
              </slot>
           </span>
            <!--如果是文本域类型-->
            <el-input
              v-else-if="item.type === 'textarea'"
              type="textarea"
              :placeholder="`${item.placeholder?item.placeholder:item.label}`"
              :disabled="item.disabled"
              :autosize="{ minRows: 4, maxRows: 6 }"
              v-model="formObject[item.prop]"
              clearable/>
            <!--如果是日期类型-->
            <el-date-picker
              v-else-if="item.type === 'date'"
              type="date"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
              v-model="formObject[item.prop]"
              :placeholder="`${item.placeholder?item.placeholder:item.label}`"
              :disabled="item.disabled"
              :size="size"
            />
            <!--如果是swich类型-->
            <el-switch
              v-else-if="item.type === 'switch'"
              v-model="formObject[item.prop]"
              inline-prompt
              active-text="是"
              inactive-text="否"
              :disabled="item.disabled"
            />
            <!--如果是普通选择框-->
            <el-select
              v-else-if="item.type === 'select'"
              v-model="formObject[item.prop]"
              clearable
              :placeholder="`${item.placeholder?item.placeholder:item.label}`"
            >
              <el-option
                v-for="(item1,index1) in item.dict"
                :key="index1"
                :label="item1.label"
                :value="item1.value"
              ></el-option>
            </el-select>
            <el-input
              v-else-if="item.type === 'number'"
              v-model.number="formObject[item.prop]"
              :disabled="item.disabled"
              :placeholder="`${item.placeholder?item.placeholder:item.label}`"
              clearable
            />
            <!--如果是图标选择类型，element plus的图标，定制开发-->
            <IconSelect
              v-else-if="item.type === 'iconSelect'"
              v-model="formObject[item.prop]"
              :placeholder="`${item.placeholder?item.placeholder:item.label}`"
              :disabled="item.disabled"
            />
            <!--如果没有type类型，默认是input类型-->
            <el-input
              v-else
              v-model="formObject[item.prop]"
              :type="`${item.prop&&item.type=='password'?'password':'text'}`"
              :disabled="item.disabled"
              :placeholder="`${item.placeholder?item.placeholder:item.label}`"
              clearable
            />
          </el-form-item>
        </template>
      </el-form>
      <template #footer>
        <slot name="foot">
          <span class="dialog-footer">
            <el-divider class="foot_divider"/>
            <el-button @click="dialogVisible = false">取消</el-button>
            <el-button type="primary" :loading="loadingButton" @click="submitForm(formRef)">确定</el-button>
          </span>
        </slot>
      </template>
    </el-dialog>
  </div>
</template>

<script lang="ts" setup>
/**
   * 特别说明
   * 1.本组件不开发表单的重置功能
   * （1）设计思路：由于本组件有特殊表单元素的插槽，因此表单对象是从父组件传入，按照vue的设计思维，重置应该在父组件完成
   * （2）技术上无法实现完美的表单重置功能。
   * 因为对话框组件重置时记录的是第一次对话框打开时的初始值，因此在先执行编辑再执行添加会出现问题。现象是添加成功并重置表单后，表单数据变为第一次打开时的数据。
   * 例如第一次打开是账户user的编辑信息，那么无论后续操作了多少次对话框，执行formRef.value!.resetFields();清空表单后，都会重置为账户user的编辑信息。
   * 因为在props无法修改form的信息，因此建议在父组件去操作手动清空数据。
   */
import {
  defineExpose, defineProps, ref, withDefaults, computed, getCurrentInstance, isRef, unref, inject, reactive,
} from 'vue';
import { TableColumn, FormHeader } from '@/types/interface/base_interface';
import { ElMessage } from 'element-plus';
import type { FormInstance } from 'element-plus';
import IconSelect from '@/components/base/form/IconSelect.vue';

const { proxy } = getCurrentInstance(); // 使用proxy代替ctx，因为ctx只在开发环境有效
  // 接收父组件的值
  interface Props {
    // form属性在20220430修改为可选属性，意图避免在父组件初始化时，避免DIALOG初始化该属性报UNDEFINED
    // 虽然在父组件使用诸如const form = ref<Department>({});可以解决此问题，但是在typescript的Department接口中，如果包含了必须的属性，则该表单必须加上必须属性的值
    // 例如：const form = ref<Department>({ id: 1, name: '大数据技术'})，导致父组件应用中缺乏灵活性
    form?: Record<string, any>, // 表单对象。
    formHeader:FormHeader[], // 表单元素集合,用于生成表单
    formInline?:boolean, // 表单是否未行内元素，对应form的inline属性。默认不是
    action?:string, // 表单的处理类型，主要有post、put等，分别代表插入和编辑。
    formRules?:any, // 整个表单的校检规则
    dialogWidth?:string, // 对话框宽度
    formLabelWidth?:string, // 表单宽度，对应el-form的label-width属性
    formLabelPosition?:string, // 表单的标签对齐方式，对应el-form的label-position属性
    title?:string, // 对话框标题,前台传入，不用加入添加或编辑的文本。添加或编辑的文本由action属性决定
    url?:string, // 表单添加或编辑的地址，要求在服务器的接口地址一样，只是方式不同。
  }
const props = withDefaults(defineProps<Props>(), {
  form: () => ({}), // 返回空对象
  formInline: false,
  title: '表单',
  action: 'post',
  dialogWidth: '500px',
  formLabelWidth: '100px',
  formLabelPosition: 'right',
  rules: reactive({}),
});
  // 接收父容器传递过来的表格刷新函数，用于刷新表格信息
let loadTableList:any = null;
if (typeof inject<()=>void>('loadTableList') !== 'undefined') {
  loadTableList = inject<()=>void>('loadTableList'); // 表格函数
}

// const formInitObject = computed(JSON.parse(JSON.stringify(unref(props.form)))); // 用于表单恢复初始值
// console.log(props.form);
// if (props.form) {
//   console.log('form初始化');
//   console.log(props.form);
//   const formInitObject = computed(JSON.parse(JSON.stringify(unref(props.form)))); // 用于表单恢复初始值
//   Object.keys(props.form).forEach((key) => {
//     console.log(key);
//   });
// }

// 表单对象
const formObject = computed(() => props.form); // 表单的实体类
// 表单初始值
// const formInitObject = computed(JSON.parse(JSON.stringify(unref(formObject)))); // 用于表单恢复初始值
// 标题，根据action生成标题和相应的操作提示
const titleResult = computed(() => {
  let title1;
  if (props.action === 'post') {
    title1 = `${props.title}添加`;
  } else if (props.action === 'put' || props.action === 'patch') {
    title1 = `${props.title}编辑`;
  } else {
    title1 = props.title;
  }
  return title1;
});
  // 状态变量
const dialogVisible = ref(false); // 对话框状态栏，true表示对话框可见
let loadingButton = ref(false); // 按钮加载状态，表示在加载中
const formRef = ref<FormInstance>(); // 定义表单的ref对象
// 打开此对话框的函数
const openDialog = () => {
  // 第一次打开对话框时会因为form没渲染导致formRef无法获取到dom对象，因此先进行判断。如果是第一次打开时则不进行表单重置，也没必要
  if (formRef.value !== undefined) {
      // 重点也是把上一次的表单验证状态去掉
      formRef.value!.resetFields();
  }
  dialogVisible.value = true;
};
const closeDialog = () => {
  dialogVisible.value = false;
};
  // 关闭对话框时候
const closed = () => {
    // 重置表单
    formRef.value!.resetFields();
    // Object.assign(formObject.value, formInitObject);
    // 关闭对话框后还要处理的信息交给父组件
    proxy.$emit('afterCloseDialog');
};
  // 移除不可用的表单元素属性,在表单元素为动态显示的时候，适合清除多余的空属性
const clearObjectDisabledParam = () => {
  props.formHeader.forEach((item: FormHeader) => { // 遍历表单元素数组
    if (item.disabled === true) { // 如果某个表单元素之不可用
      delete formObject.value[item.prop]; // 移除该属性
    }
  });
};
  // 提交表单函数,包含添加和编辑操作
  // 如果表单为disabled则不提交
const submitForm = async (formEl: FormInstance | undefined) => { // 提交表单
  if (!formEl) return;
  // 表单验证
  await formEl.validate(async (valid):Promise<boolean> => { // 如果通过表单验证
    if (valid) {
      // console.log(props.form);
      // Object.keys(props.form).forEach((key) => {
      //   console.log(key);
      // });
      if (props.url) { // 如果传入表单地址
        let resData;
        // 移除不可用的表单元素，避免提交多余数据
        clearObjectDisabledParam();
        // 提交表单前要处理的钩子函数，如果要在提交服务端前处理一些数据，可以在这里处理，例如处理关联对象的值，将departmentId处理为department.id再传递
        proxy.$emit('beforeSubmit', formObject.value);
        if (props.action === 'post') {
          resData = await new proxy.$Request(props.url, JSON.stringify(formObject.value)).post();
        } else if (props.action === 'put') {
          resData = await new proxy.$Request(props.url, JSON.stringify(formObject.value)).put();
        } else if (props.action === 'patch') {
          resData = await new proxy.$Request(props.url, JSON.stringify(formObject.value)).patch();
        }
        // 编写要在提交后操作的代码
        proxy.$emit('afterSubmit', resData);
        if (resData.success) {
          // 清空表单值
          // formRef.value!.resetFields();
          // 钩子函数，提交表单成功后的操作
          // 如果要清空值建议在这里清空
          proxy.$emit('afterSubmitSuccess');
          // 将表格重新刷新
          if (loadTableList != null) {
              loadTableList!();
          }
          if (props.action === 'put') { // 只有修改成功，才会关闭对话框，添加操作则保持对话框状态，以便继续添加。
            dialogVisible.value = false;
          }
          ElMessage({
            message: `${titleResult.value}成功`,
            type: 'success',
          });
        }
      }
      return true;
    }
    // 如果验证失败
    return false;
  });
};

defineExpose({ // 参考vue3官方文档，需要将函数暴露出去才能被父组件调用，setup模式是不暴露的
  openDialog,
  closeDialog,
});
</script>

<style lang="scss" scoped>
  .dialog{
    ::v-deep .el-dialog__header{ /*防止父容器设置了统一的右对齐，导致对话框默认的左对齐变为右对齐。*/
      text-align:left;
    }
    ::v-deep .el-dialog__body{ /*修改el-dialog对话框的body样式*/
      padding: 10px 15px; /*element中dialog组件的body默认样式是padding：30px 20px;*/
    }
    .title_divider{
      margin:20px 0 5px 0;
    }
    .foot_divider{
      margin:0 0 20px 0;
    }
  }
</style>
